#!/usr/bin/python
#
# Copyright (c) 2009 Josef Hardi. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
#-*- coding: utf-8 -*-

"""
    Robo Link Collector
    
    This robot crawls the Chromium SVN change logs and searches
    for code review page locator (URL).
    
    @copyright: 2009 Josef Hardi
    @license: GNU GPL
"""

__author__ = 'Josef Hardi <josef.hardi@gmail.com>'


import os, sys
import re

import pysvn
from optparse import OptionParser

reload(sys)
sys.setdefaultencoding('utf-8')

USAGE = os.path.basename(sys.argv[0]) + ' [options] [repository url]'
DESCRIPTION = '''
Collect code review URLs from SVN change logs.
'''


def print_output(base_repository_url, revisions):
  """Prints the string output."""
  for log_entry in svnlog(base_repository_url, revisions):
    review_url = extract_url(log_entry.message)
    
    if review_url:
      # Print the RSS link locator
      print review_url.replace('.org/', '.org/rss/issue/')


def svnlog(base_repository_url, revisions):
  """Return the log messages"""
  client = pysvn.Client()
  
  revnum_start = None
  revnum_end = None
  try:
    revnum_start = pysvn.Revision(pysvn.opt_revision_kind.number,
                                  revisions[0])
    revnum_end = pysvn.Revision(pysvn.opt_revision_kind.number,
                                revisions[1])
  except IndexError:
    revnum_end = revnum_start
  
  log_messages = client.log(base_repository_url,
                            revision_start = revnum_start,
                            revision_end = revnum_end,
                            limit=0)
  
  return log_messages


def extract_url(message):
  """Extract code review URL from SVN change log entry message.
  
  Typically, the URL is placed in the end of the log message. For example:
  
  $
  Implement the broker process that launches NaCl loader processes on 64-bit
  Windows systems.
  
  BUG=28176
  TEST=none
  Review URL: http://codereview.chromium.org/542030
  $
  
  However, in some cases, the URL is written in the middle of the log message.
  This variation sometimes make the extraction more complicated because the
  actual link sets in the end of the message. For example:
  
  $
  Lands http://codereview.chromium.org/155428 for Thiago, which changes
  from me to get the thing to work.
  
  Merging two versions of BookmarkNodeRemoved into one.
  
  BUG=None
  TEST=Remove some bookmarks from BookmarkBar or Bookmarks Manager and make
  sure everything works ok.
  
  Review URL: http://codereview.chromium.org/155572
  $
  
  To overcome this difficulty, I assign different priority for the URL
  extraction.
  """
  url_string = 'http://codereview.chromium.org/'
  general_pattern = re.compile('('+url_string+')(\d+)')
  recommended_pattern = re.compile('.*Review URL\s*:\s*('+url_string+')(\d+)\s*')
  
  url = None
  
  lines = message.split("\n")
  for line in lines:
    if general_pattern.search(line) is not None:
      url = general_pattern.search(line)
    
    if recommended_pattern.match(line) is not None:
      # Overwrite the previous URL
      url = recommended_pattern.match(line)
  
  if url is not None:
    return "".join(url.group(1, 2))  # URL+Issue id
  
  return None


def get_options(argv):
  parser = OptionParser(usage=USAGE)
  parser.description=DESCRIPTION
  parser.add_option("-r", "--revision",
                    dest="range",
                    default=None,
                    help="A single revision number or a range of revisions \
                          to parse e.g. -r400:456.")
  
  options, args = parser.parse_args(argv)
  script, args = args[0], args[1]
  return options, script, args


def main(argv=None):
  '''The main'''
  argv = argv or sys.argv
  options, script, base_repository_url = get_options(argv)
  
  # Revision option
  revisions = options.range.split(':')
  
  print_output(base_repository_url, revisions)

if __name__ == "__main__":
  sys.exit(main())
